#include "tsocket.h"
#include <assert.h>
#ifdef WIN32
#include <winsock2.h>
#define close			closesocket
#define socklen_t		int
#pragma comment(lib, "ws2_32.lib")
#else
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/tcp.h>
#define INVALID_SOCKET	-1
#endif
namespace tlib
{
bool TSocket::EnvInit()
{
#ifdef WIN32
	WSADATA wsaData;
	if (0 != ::WSAStartup(MAKEWORD(2, 2), &wsaData))
	{
		::WSACleanup();
		return false;
	}
#endif
	return true;
}

bool TSocket::EnvFree()
{
#ifdef WIN32
	::WSACleanup();
#endif
	return true;
}

int TSocket::Send(int fd, char* buff, int len)
{
	int nwritten = ::send(fd, buff, len, 0);
	assert(nwritten == len);
	return nwritten;
}

int TSocket::Recv(int fd, char* buff, int len)
{
	int nread = ::recv(fd, buff, len, 0);
	if (nread == 0)
		return -1;
	if (nread == -1)
	{
#ifdef WIN32
		int error = ::GetLastError();
		if (WSAEWOULDBLOCK == error) return 0;
#else
		if (EAGAIN == errno)
			return 0;
#endif
		return -1;
	}
	return nread;
}

int TSocket::Create()
{
	return ::socket(AF_INET, SOCK_STREAM, 0);
}

int TSocket::Bind(int fd, char* host, int port)
{
	struct sockaddr_in sock_addr;
	sock_addr.sin_family = AF_INET;
	sock_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	sock_addr.sin_port = htons(port);
	return ::bind(fd, (struct sockaddr*) &sock_addr, sizeof(struct sockaddr));//-1????
}

int TSocket::Listen(int fd, int backlog)
{
	return ::listen(fd, backlog);
}

int TSocket::Accept(int fd)
{
	int sin_size = sizeof(struct sockaddr_in);
	struct sockaddr_in client_addr;
	return ::accept(fd, (struct sockaddr*) &client_addr, (socklen_t*) &sin_size);
}

int TSocket::Connect(int fd, char* host, int port)
{
	struct sockaddr_in sock_addr;
	sock_addr.sin_family = AF_INET;
	sock_addr.sin_addr.s_addr = inet_addr(host);
	sock_addr.sin_port = htons(port);
	return ::connect(fd, (struct sockaddr*) &sock_addr, sizeof(struct sockaddr));
}

void TSocket::Close(int fd)
{
	::close(fd);
	fd = INVALID_SOCKET;
}

bool TSocket::SetNonBlock(int fd, bool on)
{
#ifdef WIN32
	unsigned long ul = on ? 1 : 0;
	return 0 == ::ioctlsocket(fd, FIONBIO, (unsigned long*)&ul);
#else
	int flags = fcntl(fd, F_GETFL, 0);
	if (on)
		return 0 == fcntl(fd, F_SETFL, flags | O_NONBLOCK);
	else
		return 0 == fcntl(fd, F_SETFL, flags & (~O_NONBLOCK & 0xffffffff));
#endif
}

bool TSocket::SetTcpDelay(int fd, bool on)
{
	int optval = on ? 1 : 0;
	return 0 == ::setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char*) &optval, sizeof(optval));
}

bool TSocket::SetReuseAddr(int fd, bool on)
{
	int optval = on ? 1 : 0;
	return 0 == ::setsockopt(fd, IPPROTO_TCP, SO_REUSEADDR, (char*) &optval, sizeof(optval));
}

bool TSocket::SetReusePort(int fd, bool on)
{
#ifndef SO_REUSEPORT
#define SO_REUSEPORT SO_REUSEADDR
#endif
	int optval = on ? 1 : 0;
	return 0 == ::setsockopt(fd, IPPROTO_TCP, SO_REUSEPORT, (char*) &optval, sizeof(optval));
}

bool TSocket::SetKeepAlive(int fd, bool on)
{
	int optval = on ? 1 : 0;
	return 0 == ::setsockopt(fd, IPPROTO_TCP, SO_KEEPALIVE, (char*) &optval, sizeof(optval));
}
}
